﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Fly.Common.Extensions
{
    /// <summary>
    /// 
    /// </summary>
    public static class CurryingExtensions
    {
        #region Two Arguments

        /// <summary>
        /// Curries the specified function.
        /// </summary>
        /// <typeparam name="T1">The type of the first argument.</typeparam>
        /// <typeparam name="T2">The type of the second argument.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="func">The function to curry.</param>
        /// <returns>The curried representation of func</returns>
        public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
        {
            if (func == null)
                throw new ArgumentNullException("func", "func is null.");

            return first => second => func(first, second);
        }

        /// <summary>
        /// Curries the specified action.
        /// </summary>
        /// <typeparam name="T1">The type of the first argument.</typeparam>
        /// <typeparam name="T2">The type of the second argument.</typeparam>
        /// <param name="action">The action to curry.</param>
        /// <returns>The curried representation of action</returns>
        public static Func<T1, Action<T2>> Curry<T1, T2>(this Action<T1, T2> action)
        {
            if (action == null)
                throw new ArgumentNullException("action", "action is null.");

            return first => second => action(first, second);
        }

        #endregion

        #region Three Arguments

        /// <summary>
        /// Curries the specified function.
        /// </summary>
        /// <typeparam name="T1">The type of the first argument.</typeparam>
        /// <typeparam name="T2">The type of the second argument.</typeparam>
        /// <typeparam name="T3">The type of the third argument.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="func">The function to curry.</param>
        /// <returns>The curried representation of func</returns>
        public static Func<T1, Func<T2, Func<T3, TResult>>> Curry<T1, T2, T3, TResult>(this Func<T1, T2, T3, TResult> func)
        {
            if (func == null)
                throw new ArgumentNullException("func", "func is null.");

            return first => second => third => func(first, second, third);
        }

        /// <summary>
        /// Curries the specified action.
        /// </summary>
        /// <typeparam name="T1">The type of the first argument.</typeparam>
        /// <typeparam name="T2">The type of the second argument.</typeparam>
        /// <typeparam name="T3">The type of the third argument.</typeparam>
        /// <param name="action">The action to curry.</param>
        /// <returns>The curried representation of action</returns>
        public static Func<T1, Func<T2, Action<T3>>> Curry<T1, T2, T3>(this Action<T1, T2, T3> action)
        {
            if (action == null)
                throw new ArgumentNullException("action", "action is null.");

            return first => second => third => action(first, second, third);
        }

        #endregion

        #region Four Arguments

        /// <summary>
        /// Curries the specified function.
        /// </summary>
        /// <typeparam name="T1">The type of the first argument.</typeparam>
        /// <typeparam name="T2">The type of the second argument.</typeparam>
        /// <typeparam name="T3">The type of the third argument.</typeparam>
        /// <typeparam name="T4">The type of the fourth argument.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="func">The function to curry.</param>
        /// <returns>The curried representation of func</returns>
        public static Func<T1, Func<T2, Func<T3, Func<T4, TResult>>>> Curry<T1, T2, T3, T4, TResult>(this Func<T1, T2, T3, T4, TResult> func)
        {
            if (func == null)
                throw new ArgumentNullException("func", "func is null.");

            return first => second => third => fourth => func(first, second, third, fourth);
        }

        /// <summary>
        /// Curries the specified action.
        /// </summary>
        /// <typeparam name="T1">The type of the first argument.</typeparam>
        /// <typeparam name="T2">The type of the second argument.</typeparam>
        /// <typeparam name="T3">The type of the third argument.</typeparam>
        /// <typeparam name="T4">The type of the fourth argument.</typeparam>
        /// <param name="action">The action to curry.</param>
        /// <returns>The curried representation of action</returns>
        public static Func<T1, Func<T2, Func<T3, Action<T4>>>> Curry<T1, T2, T3, T4>(this Action<T1, T2, T3, T4> action)
        {
            if (action == null)
                throw new ArgumentNullException("action", "action is null.");

            return first => second => third => fourth => action(first, second, third, fourth);
        }

        #endregion
    }
}
